Applications: Structure-Preserving PINNs (Paper 1) — framework + inverse/traffic headline#116
Draft
davorrunje wants to merge 18 commits into
Draft
Applications: Structure-Preserving PINNs (Paper 1) — framework + inverse/traffic headline#116davorrunje wants to merge 18 commits into
davorrunje wants to merge 18 commits into
Conversation
… (Paper 1) Recreated in the devcontainer workspace (the original branch lived only in a separate host checkout at ~/projects/PhD/pinn/mononet, never pushed; this container binds ~/projects/PhD/mononet). Consolidated final content: applications/ area + Paper 1 (framework + inverse conservation/traffic flagship), forward mechanism tier, HJB->Paper 2 with de-risking caveats, Optuna equal-budget, shallow MonoResidual, gpu-jax compute strategy, manuscript-first sequencing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New top-level applications/ area (repo-only, out of the wheel) for research
papers built on mononet, distinct from benchmarks/. Adds:
- applications/_common: seeding, metrics_io, plot_theme (framework-free)
- applications/pinn package skeleton (core, models/{jax,torch}, training,
experiments, configs, results, notebooks, paper, studies) + README/RUNBOOK
- lazy-import test: importing applications.pinn pulls in no torch/jax
- wire applications/ into mypy files + ruff include (not into --cov, like
benchmarks)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Manuscript-first per spec §11: paper/paper.md written end-to-end (abstract,
intro, related work, method, theory, experimental design, discussion) with
numerical results as explicit [[…]] placeholders, each backed by a planned
Phase 5/6 experiment. references.bib gathers the load-bearing citations;
uncertain 2026 entries marked note={VERIFY}. No code yet.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- core/admissibility.py: AdmissibilitySpec (mask + convex/concave axes) and violation() — non-negative wrong-sign spatial-variation metric (0 iff monotone in the required direction); the paper's headline metric. - core/problems/base.py: Problem protocol + register/get/available registry. - Tests cover violation semantics (bump rise, symmetry, unconstrained axis) and registry roundtrip / unknown / duplicate / sorted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
core/exact.py: exact Burgers Riemann (shock at Rankine-Hugoniot speed + rarefaction), linear advection, LWR/Greenshields Riemann (shock + rarefaction, concave flux), breaking_time, and pre-shock characteristic solve. Tests check shock speeds, self-similar rarefactions, monotonicity, the t=0 step, and the implicit characteristic relation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
core/reference_solver.py: exact Godunov interface flux (convex Burgers and concave LWR via the sonic point) with CFL-limited explicit stepping and outflow BCs. Tests: L1 convergence to exact Burgers/LWR Riemann under refinement, correct shock speed within a cell, and TV non-increasing (TVD). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…(Phase 2.5) core/sampling.py: seeded collocation (uniform / Latin-hypercube), initial and boundary points, structured eval grid, and observations() — sparse, noisy draws from a reference field for the inverse flagship. Tests cover determinism, in-domain bounds, boundary placement, and noiseless/noisy observation recovery. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
BurgersRiemann, LinearAdvection, LwrRiemann (closed-form ground truth) and BurgersSmoothShock (characteristic pre-breaking, cached Godunov reference after) — each with flux, sonic point, and admissibility mask matching the entropy solution's monotone direction. problems/__init__ registers them on import. Add reference_solver.interpolate (bilinear). Registry decorator made generic so plugins register without compile-time Problem conformance. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…hase 2.7) core/metrics.py: L1/L2, TV and TV(t) curve, Gibbs overshoot, shock-position error, mass error, field reconstruction error. core/plotting.py: pure object-oriented matplotlib figures (profiles, TV curves, field heatmap), no pyplot/global state. Adds a subprocess test asserting the whole core imports neither torch nor jax (Phase 2 checkpoint invariant). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… 3.1 torch) models/protocol.py: ModelConfig + build() dispatcher. models/torch/builders.py: VanillaMLP (also the soft architecture), WeightClipMono (inexpressive non-negative-weight baseline), HardMonoField (expressive, from mononet layers). Monotone-in-x / free-in-t via an unconstrained t-embedding fed with [x,h(t)] to a stack monotone in all inputs (mask [sign_x, +1...]). Tests verify by-construction monotonicity (both signs), weight-clip monotonicity, shapes, and t-dependence. JAX builders + cross-backend equivalence pending a both-backends environment. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…luxes Application deps move out of the shared `dev` group into opt-in groups mirroring `bench`: `applications` (shared, matplotlib) and `pinn` (includes applications + optax). Backends stay package extras; `dev` stays lean. Work on the app with `uv sync --extra jax --group pinn`. Application tests importorskip matplotlib/ optax and the backend, so the dev-only suite skips them cleanly (plotting test split into its own importorskip-guarded file). Make Problem.flux/flux_prime backend-polymorphic (drop np.asarray/np.full_like) so the PDE residual differentiates through them under jax/torch tracing; verified jax.grad(flux) == flux_prime for all three problems. Independent per-application uv projects (own lockfile/venv) recorded as the agreed follow-up to prevent cross-app version conflicts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Flax NNX VanillaMLP / WeightClipMono / HardMonoField mirroring the torch builders; protocol.build() dispatches to jax; ModelConfig.mode typed as Mode; blocks stored as nnx-tracked attributes; nnx.Param via get_value(). Tests importorskip jax/flax. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
training/losses.py: backend-agnostic LossWeights + TrainingData (collocation, IC/BC, sparse observations). training/jax_trainer.py: optax Adam loop with the PDE residual u_t + f'(u) u_x built via jax.grad input derivatives over an nnx.split pure-params function (first-order, no Hessian); residual/IC/BC/data terms + optional soft monotonicity penalty (soft baseline only). Smoke tests: loss decreases, hard-monotone field stays admissible (violation<1e-5) after training, vanilla trains too. importorskip jax/flax/optax. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…3.3 torch) training/torch_trainer.py: Adam loop with the PDE residual via autograd.grad(create_graph=True), mirroring the JAX trainer (residual/IC/BC/ data + optional soft monotonicity penalty). Smoke tests: loss decreases, hard-monotone stays admissible after training, vanilla trains. tests/.../models/test_cross_backend.py: application-level cross-backend parity (both backends satisfy the by-construction monotonicity on identical inputs). Bitwise kernel equivalence is covered upstream by tests/equivalence/; trained-run numeric agreement is an empirical Phase-6 result. Closes Phase 3 (models + trainers, both backends). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
experiments/config.py: RunConfig fully specifies one (problem,method,backend, seed) run. experiments/run.py: run_one() builds model -> assembles forward-tier training data -> trains (backend dispatch) -> predicts on the eval grid -> scores vs ground truth (l1/l2, admissibility violation, overshoot); argparse CLI. Add optuna to the pinn group and a T201 ignore for experiment CLIs. Smoke tests: valid artifact, hard-monotone violation<1e-4, soft baseline runs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…2/4.3) experiments/search.py: Optuna TPE search with an identical space + trial budget across methods (soft additionally searches its penalty weight, per the §6a fair-comparison rule); objective = L2 vs ground truth; best configs frozen to configs/ and reloadable. experiments/sweep.py: build_matrix over problem x method x backend x seed and run_matrix composing run_one. Smoke tests cover search/freeze/reload, soft-penalty inclusion, and a 2-cell sweep. Closes Phase 4. Phases 5-6 (result runs) remain and need real compute + GPU. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ucture-preserving-pinns
…ucture-preserving-pinns
davorrunje
added a commit
that referenced
this pull request
Jul 15, 2026
…try) Add a Multi-paper scoping section to all three skill specs: skills operate on a research project selected by paper-id via a docs/research/papers.md registry; paths are relative to the resolved paper root (main paper at docs/research/main/, application papers at applications/<slug>/ per the PR #116 convention). Application papers read the main paper read-only. Example paths updated to the main-paper root.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Applications: Structure-Preserving PINNs (Paper 1) — framework + inverse/traffic study
New top-level
applications/area (research papers built onmononet, out of the wheel) and its first occupant,applications/pinn: structure-preserving PINNs, where mononet's expressive hard monotonicity enforces a PDE solution's admissibility (TVD/entropy) by construction instead of via soft penalties.Draft — headline (both tiers), the residual-vs-plain field ablation, and the high-noise robustness sweep (both fields) are in. Pending: figures, a non-PINN/smoothness-prior baseline (being added), cross-backend check.
Central result: robustness under noise (two PDEs, sparsity × noise, 10-seed IQM)
Inverse reconstruction from sparse noisy observations, on LWR (concave flux) and Burgers (convex flux).
Whole-field accuracy (L¹) — the general result: from noise ≥ 0.05, hard-monotone has the lowest L¹ error in 16/16 noisy LWR cells and 15/16 noisy Burgers cells (all observation counts), margin widening with noise (n_obs=80/0.20: LWR 13.2 vs 17.0/21.5; Burgers 19.0 vs 20.6/29.3). And it is exactly admissible (violation 0) at every one of the 40 cells vs baselines oscillating to 0.9–1.3. The monotone prior regularizes the noise the baselines over-fit everywhere.
Front-weighted accuracy (L²) is PDE-dependent: clean on LWR (hard best from 0.05, e.g. 0.20: 1.39 vs 1.59/1.97) but mixed on Burgers — its steeper shock lets the oscillating baselines match/beat hard on L² at high noise (0.20: 1.90 vs 1.82, while they violate 0.86). L² is dominated by the sharp front, where a continuous monotone ramp can't beat a steep (oscillating) fit; L¹ integrates the whole field, where the prior wins broadly.
Honest summary: robust general claims are (i) lower whole-field (L¹) error under noise on both PDEs and (ii) guaranteed admissibility everywhere; the "beats on every metric" reading holds cleanly only for LWR (Burgers L² is a wash-to-slight-loss at the highest noise). We report both norms rather than lead with the flattering one.
Headline panels (MonoResidual, 10-seed IQM, equal-budget tuned)
Inverse flagship (noise 0.02): hard-monotone L¹ 3.52 / L² 0.714 / viol 0 — best + only admissible; vanilla 3.92/0.731/0.083; soft 3.95/0.723/0.104;
weight_clip22.5/2.10 (fails → the effect is expressiveness, not monotonicity per se). Forward tier (worst case): constraint pays an accuracy cost (L¹ 8.1 vs 6.1), stays oscillation-free — a mechanism check.Result strength & relevance (honest assessment)
Strong:
Threats to validity / scope:
Relevance: high for the paper's thesis (turns "admissible but not more accurate" into "better accuracy + guaranteed structure, growing with noise"); moderate for the broader field, bounded by the 1-D/single-PDE/monotone-class scope. Lift items, in priority: (1) a 2nd PDE or higher-D, (2) a real noisy dataset, (3) a within-PINN curvature-penalty control.
What's here
Framework (admissibility abstraction, problem registry, NumPy
core/, JAX+PyTorch builders/trainers with grad-clip + input normalization, tier-aware Optuna search, resume-able IQM runner/sweep); field ablation as a config knob; two tiers.Upstream fix (#100)
MonoResidualhad collapsed to a near-linear map; PR #100 fixes it (merged). Both backends use MonoResidual + input normalization.Testing
uv run pytest tests/applications→ 81 passed (Torch tests skip in gpu-jax); ruff + mypy clean (203 files); docs build passes.Known gaps (RUNBOOK/FINDINGS)
Figures (crossover curve + reconstructed field); cross-backend equivalence (needs Torch →
default); physical-bounds[0, ρ_max]metric.Figures
Eight figures in
paper/figures/, embedded in §6.2.1, regenerated bypython -m applications.pinn.experiments.figures:\includegraphics, fonts embedded Type-42; via Git LFS) and PNG (for this markdown/Sphinx preview).Also merged latest
origin/main(legacy port, Stage-2 benchmark, gate fix now on main); one test conflict resolved, branch isMERGEABLE.NGSIM real-data validation (Paper 1 §6.5 scaffolding)
Adds a real-traffic-data (NGSIM I-80) inverse-reconstruction path to attack the synthetic-noise caveat of the results above. Built TDD across 10 tasks (spec:
docs/superpowers/specs/2026-07-13-real-traffic-data-ngsim-design.md, plan:docs/superpowers/plans/2026-07-13-real-traffic-data-ngsim.md); CI-inert (synthetic fixtures only — no raw NGSIM needed to build/test):applications/pinn/data/ngsim.py— Edie aggregation (trajectories → dense ρ(x,t)), objective monotone-window scan (reusesadmissibility.violation, reportsmonotonicity_defect), Greenshields FD calibration, CLI → derived.npz(Git LFS; raw gitignored).core/problems/traffic_real.py—ngsim_waveProblemloading the.npz(lazy scipy;ground_truth= bilinear interp of the Edie field; smooth Greenshieldsf'for the residual).core/sampling.py— virtual loop-detector observation mode + disjoint held-out detectors.run.py/config.py/headline.py—--observations detectors+ held-out-detector RMSE metric.baselines.py— Adaptive Smoothing Method (Treiber–Helbing) TSE baseline.figures.py --real— reconstruction slice + window heatmap + metric bars, computing ASM + RBF inline (PDF+PNG).RUNBOOK.md— reproduction commands, exploratory-gate note, ASM-tuning caveat.Metrics mirror the synthetic sections (L¹/L²/admissibility) plus held-out-detector RMSE; monotonicity holds by disclosed window selection (single stop-and-go wave), with the reference's residual defect reported.
Remaining (human-in-the-loop, needs the raw NGSIM file): drop raw I-80 → preprocess → pass the exploratory gate → tune ASM → run headline + figures → write §6.5. The ASM baseline parameterization is deliberately deferred to that gate (documented in the RUNBOOK).
Fresh draft superseding the closed #98 (reopen was blocked after the
mainLFS history rewrite; branch content unchanged).